home *** CD-ROM | disk | FTP | other *** search
/ X User Tools / X User Tools (O'Reilly and Associates)(1994).ISO / sources / xman / search.c < prev    next >
C/C++ Source or Header  |  1994-09-27  |  10KB  |  373 lines

  1. /*
  2.  * xman - X window system manual page display program.
  3.  *
  4.  * $XConsortium: search.c,v 1.20 91/07/21 21:28:09 rws Exp $
  5.  *
  6.  * Copyright 1987, 1988 Massachusetts Institute of Technology
  7.  *
  8.  * Permission to use, copy, modify, and distribute this software and its
  9.  * documentation for any purpose and without fee is hereby granted, provided
  10.  * that the above copyright notice appear in all copies and that both that
  11.  * copyright notice and this permission notice appear in supporting
  12.  * documentation, and that the name of M.I.T. not be used in advertising or
  13.  * publicity pertaining to distribution of the software without specific,
  14.  * written prior permission.  M.I.T. makes no representations about the
  15.  * suitability of this software for any purpose.  It is provided "as is"
  16.  * without express or implied warranty.
  17.  *
  18.  * Author:    Chris D. Peterson, MIT Project Athena
  19.  * Created:   November 3, 1987
  20.  */
  21.  
  22. #include "globals.h"
  23. #include "vendor.h"
  24.  
  25. /* Map <CR> and control-M to goto begining of file. */
  26.  
  27. #define SEARCHARGS 10
  28.  
  29. FILE * DoManualSearch();
  30. static int BEntrySearch();
  31.  
  32. /*    Function Name: MakeSearchWidget
  33.  *    Description: This Function Creates the Search Widget.
  34.  *    Arguments: man_globals - the pseudo globas for this manpage.
  35.  *                 w - the widgets parent
  36.  *    Returns: the search widget.
  37.  */
  38.  
  39. void
  40. MakeSearchWidget(man_globals, parent)
  41. ManpageGlobals * man_globals;
  42. Widget parent;
  43. {
  44.   Widget dialog, command, text, cancel;
  45.   Arg arglist[1];
  46.   Cardinal num_args = 0;
  47.  
  48.   XtSetArg(arglist[0], XtNtransientFor, parent); 
  49.   man_globals->search_widget = XtCreatePopupShell(SEARCHNAME, 
  50.                           transientShellWidgetClass, 
  51.                           parent,
  52.                           arglist, 1);
  53.  
  54.   if (resources.clear_search_string) {
  55.     XtSetArg(arglist[0], XtNvalue, ""); num_args++;  
  56.   }
  57.  
  58.   dialog = XtCreateManagedWidget(DIALOG, dialogWidgetClass,
  59.                  man_globals->search_widget, 
  60.                  arglist, num_args);
  61.  
  62.   if ( (text = XtNameToWidget(dialog, "value")) == (Widget) NULL)
  63.     PopupWarning(NULL, "Could not find text widget in MakeSearchWidget.");
  64.   else
  65.     XtSetKeyboardFocus(dialog, text);
  66.  
  67.   XawDialogAddButton(dialog, MANUALSEARCH, NULL, NULL);
  68.   XawDialogAddButton(dialog, APROPOSSEARCH, NULL, NULL);
  69.   XawDialogAddButton(dialog, CANCEL, NULL, NULL);
  70.  
  71. /*
  72.  * This is a bit gross, but it get the cancel button underneath the 
  73.  * others, and forms them up to the right size..
  74.  */
  75.  
  76.   if ( ((command = XtNameToWidget(dialog, MANUALSEARCH)) == (Widget) NULL) ||
  77.        ((cancel = XtNameToWidget(dialog, CANCEL)) == (Widget) NULL) )
  78.     PopupWarning(NULL,
  79.          "Could not find manual search widget in MakeSearchWidget.");
  80.   else {
  81.     Cardinal num_args = 0;
  82.     Arg arglist[2];
  83.     static char * half_size[] = {
  84.       MANUALSEARCH, APROPOSSEARCH, NULL
  85.     };
  86.     static char * full_size[] = {
  87.       "label", "value", CANCEL, NULL
  88.     };
  89.  
  90.     XtSetArg(arglist[num_args], XtNfromVert, command); num_args++;
  91.     XtSetArg(arglist[num_args], XtNfromHoriz, NULL); num_args++;
  92.     XtSetValues(cancel, arglist, num_args);
  93.     FormUpWidgets(dialog, full_size, half_size);
  94.   }
  95.  
  96. }
  97.  
  98. /*      Function Name: SearchString
  99.  *      Description: Returns the search string.
  100.  *      Arguments: man_globals - the globals.
  101.  *      Returns: the search string.
  102.  */
  103.  
  104. static char *
  105. SearchString(man_globals)
  106. ManpageGlobals * man_globals;
  107. {
  108.   Widget dialog;
  109.  
  110.   dialog = XtNameToWidget(man_globals->search_widget, DIALOG);
  111.   if (dialog != NULL) 
  112.     return(XawDialogGetValueString(dialog));
  113.  
  114.   PopupWarning(man_globals,
  115.           "Could not get the search string, no search will be preformed.");
  116.   return(NULL);
  117. }
  118.  
  119.   
  120. /*    Function Name: DoSearch
  121.  *    Description: This function performs a search for a man page or apropos
  122.  *                   search upon search string.
  123.  *    Arguments: man_globals - the pseudo globas for this manpage.
  124.  *                 type - the type of search.
  125.  *    Returns: none.
  126.  */
  127.  
  128. #define LOOKLINES 6
  129.  
  130. /* 
  131.  * Manual searches look through the list of manual pages for the right one
  132.  * with a binary search.
  133.  *
  134.  * Apropos searches still exec man -k.
  135.  *
  136.  * If nothing is found then I send a warning message to the user, and do
  137.  * nothing.
  138.  */
  139.  
  140. FILE *
  141. DoSearch(man_globals,type)
  142. ManpageGlobals * man_globals;
  143. int type;
  144. {
  145.   char cmdbuf[BUFSIZ],*mantmp,*manpath;
  146.   char tmp[BUFSIZ],path[BUFSIZ];
  147.   char string_buf[BUFSIZ], cmp_str[BUFSIZ], error_buf[BUFSIZ];
  148.   char * search_string = SearchString(man_globals);
  149.   FILE * file;
  150.   int count;
  151.   Boolean flag;
  152.  
  153.   if (search_string == NULL) return(NULL);
  154.  
  155.   /* If the string is empty or starts with a space then do not search */
  156.  
  157.   if ( streq(search_string,"") ) {
  158.     PopupWarning(man_globals, "Search string is empty.");
  159.     return(NULL);
  160.   }
  161.  
  162.   if (search_string[0] == ' ') {
  163.     PopupWarning(man_globals, "First character cannot be a space.");
  164.     return(NULL);
  165.   }
  166.  
  167.   strcpy(tmp, MANTEMP);        /* get a temp file. */
  168.   (void) mktemp(tmp);
  169.   mantmp = tmp;
  170.  
  171.   /* set the command */
  172.  
  173.   manpath=getenv("MANPATH");
  174.   if (manpath == NULL || streq(manpath,"") ) {
  175.     strcpy(path,SYSMANPATH);
  176. #ifdef LOCALMANPATH
  177.     strcat(path,":");
  178.     strcat(path,LOCALMANPATH);
  179. #endif
  180.   } else {
  181.     strcpy(path,manpath);
  182.   }
  183.  
  184.   if (type == APROPOS) {
  185.     char label[BUFSIZ];
  186.  
  187.     sprintf(label,"Results of apropos search on: %s", search_string);
  188.  
  189. #ifdef NO_MANPATH_SUPPORT    /* not quite correct, but the best I can do. */
  190.     sprintf(cmdbuf, APROPOS_FORMAT, search_string, mantmp);
  191. #else
  192.     sprintf(cmdbuf, APROPOS_FORMAT, path, search_string, mantmp);
  193. #endif
  194.  
  195.     if(system(cmdbuf) != 0) {    /* execute search. */
  196.       sprintf(error_buf,"Something went wrong trying to run %s\n",cmdbuf);
  197.       PopupWarning(man_globals, error_buf);
  198.     }
  199.  
  200.     if((file = fopen(mantmp,"r")) == NULL)
  201.       PrintError("lost temp file? out of temp space?");
  202.  
  203. /* 
  204.  * Since we keep the FD open we can unlink the file safely, this
  205.  * will keep extra files out of /tmp. 
  206.  */
  207.  
  208.     unlink(mantmp);
  209.  
  210.     sprintf(string_buf,"%s: nothing appropriate", search_string);
  211.  
  212.     /*
  213.      * Check first LOOKLINES lines for "nothing appropriate".
  214.      */
  215.   
  216.     count = 0;
  217.     flag = FALSE;
  218.     while ( (fgets(cmp_str, BUFSIZ, file) != NULL) && (count < LOOKLINES) ) {
  219.       if ( cmp_str[strlen(cmp_str) - 1] == '\n') /* strip off the '\n' */
  220.       cmp_str[strlen(cmp_str) - 1] = '\0';
  221.  
  222.       if (streq(cmp_str, string_buf)) {
  223.     flag = TRUE;
  224.     break;
  225.       }
  226.       count++;
  227.     }
  228.  
  229.     /*
  230.      * If the file is less than this number of lines then assume that there is
  231.      * nothing apropriate found. This does not confuse the apropos filter.
  232.      */
  233.  
  234.     if (flag) {
  235.       fclose(file);
  236.       file = NULL;
  237.       ChangeLabel(man_globals->label,string_buf);
  238.       return(NULL);
  239.     }
  240.   
  241.     strcpy(man_globals->manpage_title,label);
  242.     ChangeLabel(man_globals->label,label);
  243.     fseek(file, 0L, 0);        /* reset file to point at top. */
  244.   }
  245.   else {            /* MANUAL SEACH */
  246.     file = DoManualSearch(man_globals, search_string);
  247.     if (file == NULL) {
  248.       sprintf(string_buf,"No manual entry for %s.", search_string);
  249.       ChangeLabel(man_globals->label, string_buf);
  250.       if (man_globals->label == NULL)
  251.     PopupWarning(man_globals, string_buf);
  252.       return(NULL);
  253.     }
  254.   }
  255.  
  256.   if (resources.clear_search_string) {
  257.     Arg arglist[1];
  258.     Widget dialog;
  259.  
  260.     dialog = XtNameToWidget(man_globals->search_widget, DIALOG);
  261.     if (dialog == NULL) 
  262.       PopupWarning(man_globals, "Could not clear the search string.");
  263.  
  264.     XtSetArg(arglist[0], XtNvalue, "");
  265.     XtSetValues(dialog, arglist, (Cardinal) 1);
  266.   }
  267.  
  268.   return(file);
  269. }
  270.  
  271. /*    Function Name: DoManualSearch
  272.  *    Description: performs a manual search.
  273.  *    Arguments: man_globals - the manual page specific globals.
  274.  *    Returns: the filename of the man page.
  275.  */
  276.  
  277. #define NO_ENTRY -100
  278.  
  279. FILE * 
  280. DoManualSearch(man_globals, string)
  281. ManpageGlobals *man_globals;
  282. char * string;
  283. {
  284.   int e_num = NO_ENTRY;
  285.   int i;
  286.  
  287. /* search current section first. */
  288.   
  289.   i = man_globals->current_directory;
  290.   e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries);
  291.  
  292. /* search other sections. */
  293.  
  294.   if (e_num == NO_ENTRY) {
  295.     i = 0;            /* At the exit of the loop i needs to
  296.                    be the one we used. */
  297.     while ( TRUE ) {
  298.       if (i == man_globals->current_directory)
  299.     if (++i >= sections) return(NULL);
  300.       e_num = BEntrySearch(string, manual[i].entries, manual[i].nentries);
  301.       if (e_num != NO_ENTRY) break;
  302.       if (++i >= sections) return(NULL);
  303.     }
  304.  
  305. /*
  306.  * Manual page found in some other section, unhighlight the current one.
  307.  */
  308.     if ( man_globals->manpagewidgets.box != NULL)
  309.       XawListUnhighlight(
  310.     man_globals->manpagewidgets.box[man_globals->current_directory]);
  311.   }
  312.   else {
  313.     /* 
  314.      * Highlight the element we are searching for if it is in the directory
  315.      * listing currently being shown.
  316.      */
  317.     if ( man_globals->manpagewidgets.box != NULL)
  318.       XawListHighlight(man_globals->manpagewidgets.box[i], e_num);
  319.   }
  320.   return(FindManualFile(man_globals, i, e_num));
  321. }
  322.  
  323. /*    Function Name: BEntrySearch
  324.  *    Description: binary search through entries.
  325.  *    Arguments: string - the string to match.
  326.  *                 first - the first entry in the list.
  327.  *                 number - the number of entries.
  328.  *    Returns: a pointer to the entry found.
  329.  */
  330.  
  331. static int
  332. BEntrySearch(string, first, number)
  333. char * string;
  334. char ** first;
  335. int number;
  336. {
  337.   int check, cmp, len_cmp, global_number;
  338.   char *head, *tail;
  339.   
  340.   global_number = 0;
  341.   while (TRUE) {
  342.  
  343.     if (number == 0) {
  344.       return(NO_ENTRY);        /* didn't find it. */
  345.     }
  346.  
  347.     check = number/2;
  348.  
  349.     head = rindex(first[ global_number + check ], '/');
  350.     if (head == NULL) 
  351.       PrintError("index failure in BEntrySearch");
  352.     head++;
  353.  
  354.     tail = rindex(head, '.');
  355.     if (tail == NULL) 
  356.       /* not an error, some systems (e.g. sgi) have only a .z suffix */
  357.       tail = head + strlen(head);
  358.  
  359.     cmp = strncmp(string, head, (tail - head));
  360.     len_cmp = strlen(string) - (int) (tail - head);
  361.  
  362.     if ( cmp == 0 && len_cmp == 0) {
  363.       return(global_number + check);
  364.     }
  365.     else if ( cmp < 0 || ((cmp == 0) && (len_cmp < 0)) ) 
  366.       number = check;
  367.     else /* cmp > 0 || ((cmp == 0) && (len_cmp > 0)) */ {
  368.       global_number += (check + 1);
  369.       number -= ( check + 1 );
  370.     }
  371.   }
  372. }
  373.